﻿using System;

namespace PoolKit
{
    internal interface IResourceCollection<TResource> : IDisposable
    {
        /// <summary>
        /// Tries to take a resource out of resource collection. If this succeeds, the collection still keeps track of the resource,
        /// as it can be returned back to it.
        /// </summary>
        /// <param name="resource"></param>
        /// <returns></returns>
        /// <exception cref="ObjectDisposedException"></exception>
        bool TryTake(out TResource resource);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="resource"></param>
        /// <exception cref="CannotAcceptResourceException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        void AcceptBack(TResource resource);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="resourceBeingSubstituted"></param>
        /// <param name="substitution"></param>
        /// <exception cref="CannotAcceptResourceException"></exception>
        /// <exception cref="ReplacingResourceProducesDuplicateException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        void AcceptSubstitution(TResource resourceBeingSubstituted, TResource substitution);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="newResource"></param>
        /// <exception cref="CannotAddResourceException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        void Add(TResource newResource);

        /// <summary>
        /// Note: it's assumed that after calling this one, the pool will actually call AcceptBack or AcceptBackBroken.
        /// </summary>
        /// <param name="resource"></param>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        void ReserveRightToReturnResource(TResource resource);

        /// <summary>
        /// Count of resources that currently are in collection (that is, without those which have been taken away)
        /// </summary>
        int Count { get; }

        event EventHandler ItemCountUp;

        event EventHandler ItemCountDown;

        /// <summary>
        /// Tries to evict a resource that's in the collection. If this succeeds, the collection forgets it ever had the resource.
        /// </summary>
        /// <param name="resourceToEvict"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        bool TryEvict(TResource resourceToEvict);

        /// <summary>
        /// Register a resource making it look like it had been in collection at some point
        /// and has been taken from it.
        /// </summary>
        /// <param name="resource"></param>
        /// <exception cref="CannotAddResourceException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        void RegisterResourceAsTaken(TResource resource);

        /// <summary>
        /// Makes the collection forget the resource that is currently out of collection.
        /// </summary>
        /// <param name="resource"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="CannotForgetResourceException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        void Forget(TResource resource);
    }
}